﻿// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// Copyright (c) 2011, Dr. Masroor Ehsan
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the author nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 
// $Author:$
// $Id:$
// $Rev:$
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

using System;
using System.Collections.Generic;
using CuttingEdge.Conditions;
using MaxTrader.Core;

namespace MaxTrader.BusinessObjects
{
    public class ShareAccount : AbstractAccount
    {
        private readonly Dictionary<StockSymbol, Position> _positions;

        public ShareAccount()
        {
            _positions = new Dictionary<StockSymbol, Position>();
            AccountType = AccountType.ShareAccount;
        }

        public ShareAccount(string accountName, Currency currency) : this()
        {
            AccountName = accountName;
            Currency = currency;
        }

        /// <summary>
        ///   Create a new cash account with the Bangladeshi currency.
        /// </summary>
        public ShareAccount(string accountName) : this()
        {
            AccountName = accountName;
            Currency = Currency.Bdt;
        }

        public virtual Dictionary<StockSymbol, Position> GetPositions
        {
            get { return _positions; }
        }

        public override Money GetValue()
        {
            throw new NotImplementedException();
        }

        public override Money GetValue(IQuoteSource quoteBundle, TradingDate date)
        {
            throw new NotImplementedException();
        }

        public override void ApplyTransaction(AbstractTransaction transaction)
        {
            Condition
                .Requires(transaction, "transaction")
                .IsOfType(typeof (ShareTransaction));
            Condition
                .Requires(transaction.Amount.Currency, "transaction.Amount.Currency")
                .Evaluate(c => c.Equals(Currency));

            var shareTrans = transaction as ShareTransaction;
            Condition
                .Requires(shareTrans.NumberOfShares, "NumberOfShares")
                .IsGreaterThan(0);
            var symbol = shareTrans.Symbol;

            Position holding;
            _positions.TryGetValue(symbol, out holding);

            switch (shareTrans.Category)
            {
                case TransactionCategory.Accumulate:
                    var avgPrice = shareTrans.Amount/shareTrans.NumberOfShares;
                    var avgCommission = shareTrans.TradeCost/shareTrans.NumberOfShares;
                    var avgCost = (shareTrans.Amount + shareTrans.TradeCost)/shareTrans.NumberOfShares;

                    if (holding != null)
                        holding.Accumulate(shareTrans.NumberOfShares, avgPrice, avgCommission);
                    else
                    {
                        _positions.Add(symbol,
                                       new Position(Portfolio, symbol, shareTrans.Date, shareTrans.NumberOfShares,
                                                    avgPrice, avgCommission, avgCost));
                    }
                    break;
                case TransactionCategory.DividendDrip:
                    if (holding != null)
                        holding.Accumulate(shareTrans.NumberOfShares, 0, 0);
                    else
                    {
                        _positions.Add(symbol,
                                       new Position(Portfolio, symbol, shareTrans.Date, shareTrans.NumberOfShares, 0, 0,
                                                    0));
                    }
                    break;
                case TransactionCategory.Reduce:
                    Condition.Requires(holding).IsNotNull("holding");

                    holding.Reduce(shareTrans.NumberOfShares, shareTrans.TradeCost/shareTrans.NumberOfShares);

                    // do we have any left? if not remove stock holding from holdings
                    if (holding.NumberOfShares <= 0)
                        _positions.Remove(symbol);
                    break;
            }
        }

        /// <summary>
        ///   Return the stock holding for a given symbol
        /// </summary>
        public virtual Position GetStockHolding(StockSymbol symbol)
        {
            Position holding;
            _positions.TryGetValue(symbol, out holding);
            return holding;
        }

        /// <summary>
        ///   Return whether the account is holding the current symbol
        /// </summary>
        public virtual bool IsHolding(StockSymbol symbol)
        {
            return _positions.ContainsKey(symbol);
        }

        /// <summary>
        ///   Removing all transactions means removing all stocks from our account
        /// </summary>
        public virtual void RemoveAllTransactions()
        {
            _positions.Clear();
        }

        public override object Clone()
        {
            throw new NotImplementedException();
        }

        public override bool Equals(AbstractAccount other)
        {
            throw new NotImplementedException();
        }
    }
}